package de.gbeine.java.depview.beans.converter;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import lombok.extern.slf4j.Slf4j;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;

import de.gbeine.java.depview.event.ChangeStatusEvent;
import de.gbeine.java.depview.event.NewModelEvent;
import de.gbeine.java.depview.event.ParseFileEvent;

/**
 * The SaxConverter parses a XML file using SAX.
 *
 * The SaxConverter subscribes to an EventBus and starts on a ParseFileEvent.
 * It sends the Events NewModel and ChangeStatus to the EventBus.
 */
@Slf4j
public final class SaxConverter {

    /**
     * The SAXParserFactory used to create parsers.
     */
    private static final SAXParserFactory SPF = SAXParserFactory.newInstance();

    /**
     * The EventBus to sent messages to when parsing is finished.
     */
    private EventBus eventBus;

    /**
     * Creates a new SaxConverter bean.
     * @param pEventBus The EventBus to sent messages to.
     */
    public SaxConverter(final EventBus pEventBus) {
        SaxConverter.SPF.setValidating(true);
        this.eventBus = pEventBus;
    }

    /**
     * Listen for an event triggers parsing of a file.
     *
     * If the parsing of the file is successful,
     * a NewModelEvent and a ChangeStatusEvent are sent to the EventBus.
     *
     * TODO
     * In case the parsing fails, a ChangeStatus event should also be sent.
     * This event contains the error reason.
     *
     * @param pParseFileEvent The event containing the file to parse.
     */
    @Subscribe
    public void onParseFileEvent(final ParseFileEvent pParseFileEvent) {
        final File file = pParseFileEvent.getFile();
        final SaxHandler saxHandler = new SaxHandler();
        SaxConverter.log.debug("Received event to parse file {}", file.getName());
        try {
            InputSource is = new InputSource(new FileInputStream(file));
            XMLReader reader = SaxConverter.SPF.newSAXParser().getXMLReader();
            reader.setContentHandler(saxHandler);
            reader.parse(is);
            this.eventBus.post(new NewModelEvent(this, Collections.unmodifiableSet(saxHandler.getPackages())));
            this.eventBus.post(new ChangeStatusEvent(this, "Opened file: " + file.getName()));
        } catch (final FileNotFoundException fnfe) {
            SaxConverter.log.error(fnfe.getMessage());
        } catch (final ParserConfigurationException pce) {
            SaxConverter.log.error(pce.getMessage());
        } catch (final IOException ioe) {
            SaxConverter.log.error(ioe.getMessage());
        } catch (final SAXException saxe) {
            SaxConverter.log.error(saxe.getMessage());
        }
    }
}
